﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Reflection;
using BesAsm.MasterData.Gis;
using BesAsm.MasterData.Entities;
using BesAsm.Framework.ArcGisUtilities;
using BesAsm.Framework.Reporting;
using BesAsm.Emgaats.Core;
using ESRI.ArcGIS.Framework;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.ArcMapUI;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using BesAsm.Framework.Reporting;


namespace BesAsm.Emgaats.Core
{
 
  /// <summary>
  /// Provides plug-in functionality for Emgaats extensions. Maintains a collection of all
  /// ArcGIS extensions in the current session which inherit from EmgaatsExtensionBase. Also
  /// provides a central location for Emgaats extensions to share data.
  /// </summary>
  public sealed class Mediator
  {
    private static readonly Mediator _mediator = new Mediator();
    private static bool initialized = false;

    #region Events

    public delegate void ExtensionRegisteredEventHandler(object sender, EmgaatsExtensionArgs e);
    public event ExtensionRegisteredEventHandler ExtensionRegistered;
    public event ExtensionRegisteredEventHandler ExtensionUnregistered;    

    private void OnExtensionRegistered(EmgaatsExtensionArgs e)
    {
      if (ExtensionRegistered != null)
        ExtensionRegistered(this, e);
    }

    private void OnExtensionUnregistered(EmgaatsExtensionArgs e)
    {
      if (ExtensionUnregistered != null)
        ExtensionUnregistered(this, e);
    }

    /// <summary>
    /// Occurs when a model is loads (via new or open)
    /// </summary>
    public static event EventHandler ModelLoaded;

    private void OnModelLoaded(EventArgs e)
    {
      if (ModelLoaded != null)
        ModelLoaded(this, e);
      if (Model.State != ModelState.Null)
      {
        Messenger.ReportMessage("Model changed to " + Model.GetPathIfSet(ModelPathComponent.ConfigFile),
          ReportableMessageType.Info);
      }
      else
      {
        Messenger.ReportMessage("Null model created", ReportableMessageType.Info);
      }
    }

    #endregion
    
    private Model _model;
    private BindingList<IEmgaatsExtension> _registeredExtensions = new BindingList<IEmgaatsExtension>();

    private static IApplication _app;

    private Mediator()
    {
      try
      {
      }
      catch (Exception ex)
      {
        throw ex;
      }
    }

    /// <summary>
    /// Registers an IEmgaatsExtension with the mediator and provides access to the
    /// singleton Mediator object
    /// </summary>
    /// <param name="callingExtension">The calling extension.</param>
    /// <returns>The singleton instance of the Mediator</returns>
    public static Mediator AttachToMediator(IEmgaatsExtension callingExtension)
    {
      if (!(callingExtension is IEmgaatsExtension))
        throw new Exception("Emgaats Extensions must implement from IEmgaatsExtension");

      _mediator.RegisterExtension(callingExtension);

      return _mediator;
    }

    internal static Mediator InitializeMediator()
    {      
      if (!initialized)
      {        
        ConfigureEnterpriseLibrary();        

        initialized = true;
      }

      return _mediator;
    }

    private static void ConfigureEnterpriseLibrary()
    {
      try
      {
        var configBuilder = new ConfigurationSourceBuilder();

        configBuilder.ConfigureSecurity()
          .AuthorizeUsingRuleProviderNamed("NetTiers.RuleProvider")
          .CacheSecurityInCacheStoreNamed("BesAsm.MasterData.Entities.EntityCache")
          .WithOptions.SlidingExpiration(new TimeSpan(0, 10, 0))
          .WithOptions.AbsoluteExpiration(new TimeSpan(0, 60, 0))
          .SetAsDefault();

        configBuilder.ConfigureLogging()
          .SpecialSources.AllEventsCategory
            .SendTo
            .FlatFile("FlatFileTraceListener")
              .ToFile(LogFileName)
              .WithHeader("----------------------------------------")
              //.WithFooter("----------------------------------------")
              .FormatWith(new FormatterBuilder()
                .TextFormatterNamed("DefaultFormatter")
                //.UsingTemplate("Timestamp: {timestamp}\r\nMessage: {message}\r\nCategory: {category}\r\nPriority: {priority}\r\nEventId: {eventid}\r\nSeverity: {severity}\r\nTitle:{title}\r\nMachine: {machine}\r\nApplication Domain: {appDomain}\r\nProcess Id: {processId}\r\nProcess Name: {processName}\r\nWin32 Thread Id: {win32ThreadId}\r\nThread Name: {threadName}\r\nExtended Properties: {dictionary({key} - {value}\r\n)}"))
                .UsingTemplate("{timestamp} / {severity}\r\n{message}"))
              .WithTraceOptions(System.Diagnostics.TraceOptions.None);

        configBuilder.ConfigureExceptionHandling()
          .GivenPolicyWithName("ServiceLayerExceptionPolicy")
          .ForExceptionType<System.Exception>()
            .LogToCategory("Exceptions")
              .WithSeverity(System.Diagnostics.TraceEventType.Error)
              .UsingEventId(100)
              .UsingTitle("BesAsm.MasterData Exception Handling")
              .UsingExceptionFormatter
                <Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.XmlExceptionFormatter>()
              .WithPriority(0)
            .ThenNotifyRethrow();

        configBuilder.ConfigureCaching()
          .ForCacheManagerNamed("BesAsm.MasterData.Entities.EntityCache")
            .UseAsDefaultCache()
            .WithOptions
              .StartScavengingAfterItemCount(1000)
              .PollWhetherItemsAreExpiredIntervalSeconds(60)
              .WhenScavengingRemoveItemCount(10);

        var configSource = new DictionaryConfigurationSource();
        configBuilder.UpdateConfigurationWithReplace(configSource);
        EnterpriseLibraryContainer.Current = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);

      }
      catch (Exception ex)
      {
        throw ex;
      }
    }

    private void RegisterExtension(IEmgaatsExtension callingExtension)
    {
      if (!_registeredExtensions.Contains(callingExtension))
      {
        _registeredExtensions.Add(callingExtension);
        OnExtensionRegistered(
          new EmgaatsExtensionArgs(callingExtension));

        Messenger.ReportMessage("Registered extension " + callingExtension.Caption,
        Framework.Reporting.ReportableMessageType.Info);
      }
      
      //callingExtension.ShuttingDown += new EventHandler(callingExtension_ShuttingDown);
    }   

    /// <summary>
    /// Handles the ShuttingDown event of the callingExtension control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    void callingExtension_ShuttingDown(object sender, EventArgs e)
    {
      IEmgaatsExtension emgExtension = (IEmgaatsExtension)sender;
      _registeredExtensions.Remove(emgExtension);
      OnExtensionUnregistered(
        new EmgaatsExtensionArgs(emgExtension));
    }

    /// <summary>
    /// Gets a collection of the registered extensions.
    /// </summary>
    public static IEnumerable<IEmgaatsExtension> RegisteredExtensions
    {
      get { return _mediator._registeredExtensions.AsEnumerable(); }
    }

    /// <summary>
    /// Gets the model.
    /// </summary>
    public static Model Model
    {
      get
      {
        if (_mediator._model == null)
        {
          _mediator._model = new Model();
          _mediator.OnModelLoaded(EventArgs.Empty);
        }
        return _mediator._model;
      }
      internal set
      {
        _mediator._model = value;
        _mediator.OnModelLoaded(EventArgs.Empty);
      }
    }

    /// <summary>
    /// Sets the state of the Model. The ModelStateChanged event will be raised.
    /// </summary>
    /// <param name="state">The new Model state.</param>
    public void SetModelState(ModelState state)
    {
      // TODO: smells funny that the mediator can set this...seems more like something the Model
      // itself should be managing
      _model.State = state;
    }

    /// <summary>
    /// Gets a reference to the current ArcGIS IApplication
    /// </summary>
    public static IApplication ArcApp
    {
      get { return _app; }
    }

    /// <summary>
    /// Gets the version of the system (master database)
    /// </summary>
    public static string SystemVersion
    {
      get
      {
        // TODO: Check the database for system version and return it here
        return Properties.Settings.Default.EmgaatsSystemVersion;
      }
    }

    public static string MasterConnectionString
    {
      get
      {
        return Properties.Settings.Default.UserConnectionString;
      }
    }

    public static string LogFileName
    {
      get
      {
        if (Properties.Settings.Default.LogFile.Length == 0)
        {
          Properties.Settings.Default.LogFile =
            String.Format("{0}{1}{2}_Emgaats.log",
            Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
            System.IO.Path.DirectorySeparatorChar,
            Environment.UserName);
          Properties.Settings.Default.Save();
        }
        return Properties.Settings.Default.LogFile;
      }
    }

    #region Event Wiring for New and Open Documents
    // ArcGIS Snippet Title:
    // Add Event Wiring for New and Open Documents
    // 
    // Long Description:
    // Wire event when a document is created or opened in ArcGIS desktop applications.
    // 
    // Add the following references to the project:
    // ESRI.ArcGIS.ArcMapUI
    // ESRI.ArcGIS.Framework
    // 
    // Intended ArcGIS Products for this snippet:
    // ArcGIS Desktop (ArcEditor, ArcInfo, ArcView)
    // 
    // Applicable ArcGIS Product Versions:
    // 9.2
    // 9.3
    // 
    // Required ArcGIS Extensions:
    // (NONE)
    // 
    // Notes:
    // This snippet is intended to be inserted inside a Method.
    // It is not intended to be added to the base level of the Class.
    // 

    /// <summary>
    /// Event member variable
    /// </summary>
    /// <remarks></remarks>
    private ESRI.ArcGIS.ArcMapUI.IDocumentEvents_Event _docEvents = null;

    /// <summary>
    /// Set up the wiring of the events.
    /// </summary>
    /// <param name="myDocument"></param>
    /// <remarks></remarks>
    private void SetUpDocumentEvent(ESRI.ArcGIS.Framework.IDocument myDocument)
    {
      _docEvents = myDocument as ESRI.ArcGIS.ArcMapUI.IDocumentEvents_Event;
      _docEvents.NewDocument += new IDocumentEvents_NewDocumentEventHandler(OnNewDocument);
      _docEvents.OpenDocument += new IDocumentEvents_OpenDocumentEventHandler(OnOpenDocument);
      _docEvents.CloseDocument += new IDocumentEvents_CloseDocumentEventHandler(OnCloseDocument);
    }

    /// <summary>
    /// The NewDocument event handler. 
    /// </summary>
    /// <remarks></remarks>
    protected void OnNewDocument()
    {
      ESRI.ArcGIS.Framework.IDocument theDocument = _docEvents as ESRI.ArcGIS.Framework.IDocument;
    }

    /// <summary>
    /// The OpenDocument event handler.
    /// </summary>
    /// <remarks></remarks>
    protected void OnOpenDocument()
    {
      ESRI.ArcGIS.Framework.IDocument theDocument = _docEvents as ESRI.ArcGIS.Framework.IDocument;
    }

    protected void OnCloseDocument()
    {
      ESRI.ArcGIS.Framework.IDocument theDocument = _docEvents as ESRI.ArcGIS.Framework.IDocument;
    }

    #endregion
  }
}
